/*
 * @FilePath     : /study_code/vue/principle4.js
 * @Description  : 迷你观察者模式
 * @Date         : 2025-04-29 17:05:35
 * @Author       : Fang YingGui
 * @LastEditors  : fangyinggui 18715384437@163.com
 * @LastEditTime : 2025-04-29 17:05:42
 */
function isObject(obj) {
  return typeof obj === 'object' && !Array.isArray(obj) && obj !== null && obj !== undefined
}

/**
 * @brief: 观察者函数
 * @param {*} obj
 * @return {*}
 */
function observe(obj) {
  if (!isObject(obj)) {
    throw new TypeError()
  }

  Object.keys(obj).forEach(key => {
    let internalValue = obj[key]
    let dep = new Dep()
    Object.defineProperty(obj, key, {
      get() {
        dep.depend()
        console.log('读取一次 obj 的值', internalValue)
        return internalValue
      },
      set(newValue) {
        const isChanged = internalValue !== newValue
        if (isChanged) {
          internalValue = newValue
          console.log('设置一次 obj 的值', newValue)
          console.log('Set', dep)
          dep.notify()
        }
      },
    })
  })
  console.log('对象重写后', obj)
}

window.Dep = class Dep {
  constructor() {
    this.subscribers = new Set()
  }

  depend() {
    if (activeUpdate) {
      // register the current active update as a subscriber
      this.subscribers.add(activeUpdate)
    }
  }

  notify() {
    // run all subscriber functions
    this.subscribers.forEach(subscriber => subscriber())
  }
}

let activeUpdate

function autorun(update) {
  function wrappedUpdate() {
    activeUpdate = wrappedUpdate
    update()
    activeUpdate = null
  }
  wrappedUpdate()
}

// --- 使用
var user = {
  name: 'lotut',
  age: 15,
}

console.log(1, user)
/*
1、观察者函数执行前，user 对象还是一个普通的对象字面量；
注意：此时 user 对象只有 name, age 属性，没有 get, set 描述符 （注释 observe(user) 后查看）
*/

observe(user)
console.log(2, user)

var unamedom = document.getElementById('uname')

autorun(() => {
  // 这里的代码是 “响应式代码”
  unamedom.innerHTML = user.name
  alert('更新视图')
  console.log('autorun - "响应式依赖"')
})

var btn = document.getElementById('btn')
btn.addEventListener('click', function () {
  user.name = 'hflotut'
  console.log('设置一次数据值', user)
})